home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-21 / xcb-20.zip / XCB.C < prev    next >
C/C++ Source or Header  |  1992-11-11  |  23KB  |  917 lines

  1. /*
  2.  * xcb: Copyright (C) 1992 by Farrell McKay.
  3.  * XView modifications provided by Danny Vanderryn.
  4.  *
  5.  * Simple X interface to the cut buffers in an X server.
  6.  * The program creates a window subdivided into a number of subwindows,
  7.  * one per cut buffer.  The user may copy cut buffer contents around
  8.  * using mouse buttons 1 and 2, or rotate the buffers using mouse
  9.  * button 3.  Buffers may be cleared by using Shift-button 2.
  10.  *
  11.  * Note that this program assumes the cut buffers contain textual
  12.  * information, and displays buffer contents using the XDrawString
  13.  * function.  It is not suitable for use in any other environment.
  14.  *
  15.  * Permission to use, copy, modify, and distribute this software and its
  16.  * documentation for any purpose and without fee is hereby granted, provided
  17.  * that the above copyright notice appears in all copies and that both that
  18.  * copyright notice and this permission notice appear in supporting
  19.  * documentation.  This software is provided "as is" without express or
  20.  * implied warranty.
  21.  */
  22. #include <ctype.h>
  23. #include <stdio.h>
  24. #include <string.h>            /* for strcmp() */
  25. #ifdef    __STDC__
  26. #include <stdlib.h>            /* for exit()... */
  27. #endif
  28. #include <unistd.h>            /* for read(), write() */
  29. #include <X11/Xatom.h>            /* for pre-defined atom names */
  30. #include <X11/StringDefs.h>        /* for XtNforeground et.al.*/
  31. #include <X11/Intrinsic.h>
  32. #include <X11/Xaw/Form.h>
  33. #include "cb.h"
  34. #ifdef MSDOS
  35. #include "patchlev.h"      /* KSW 11/10/1992  4:07pm. */
  36. #else
  37. #include "patchlevel.h"
  38. #endif /* MSDOS */
  39.  
  40. #define    _printf        (void) printf
  41. #define    _fprintf    (void) fprintf
  42. #define    _sprintf    (void) sprintf
  43.  
  44. #define    eq(a,b)        (strcmp((a),(b)) == 0)
  45. #define    min(a,b)    ((a) < (b)? (a): (b))
  46. #define    max(a,b)    ((a) > (b)? (a): (b))
  47.  
  48. #define    XtNbufferCount    "bufferCount"        /* Application resources */
  49. #define    XtCBufferCount    "BufferCount"
  50. #define    XtNlayout    "layout"
  51. #define    XtCLayout    "Layout"
  52.  
  53. #define    PGM_NAME    "xcb"
  54. #define    PGM_CLASS    "Xcb"
  55. #define    BUFINC        2048
  56.  
  57. typedef unsigned char    uchar;
  58.  
  59. static Display        *dpy;
  60. static Window        root;
  61. static XtAppContext    app;
  62. static Widget        top, box, *wdg;
  63. static Atom        *atom;
  64. static int        natoms, nbuffs;
  65. static int        argc;
  66. static char        **argv;
  67.  
  68. #ifdef XVIEW
  69. static Atom        caret, clipboard, yield;
  70. static Atom        length, lengthc;
  71. static Atom        text, ctext;
  72. #endif
  73.  
  74.  
  75. /*
  76.  * Fetch the contents of cut buffer n from the root window.
  77.  */
  78. static char *
  79. fetch_buffer(a, nb)
  80. Atom    a;
  81. int    *nb;
  82. {
  83.     Atom        actl_type;
  84.     int        actl_format;
  85.     unsigned long    nitems, after;
  86.     char        *data;
  87.  
  88.     *nb = 0;
  89.     if (XGetWindowProperty(dpy, root, a,
  90.             0L, 10000000L, False, XA_STRING,
  91.             &actl_type, &actl_format,
  92.             &nitems, &after, (uchar **) &data) != Success)
  93.         return NULL;
  94.  
  95.     if (actl_type == XA_STRING && actl_format != 32) {
  96.         *nb = nitems;
  97.         return data;
  98.     }
  99.     if (data != NULL)
  100.         XFree(data);
  101.     return NULL;
  102. }
  103.  
  104. /*
  105.  * Store the string p into cut buffer n on the root window.
  106.  */
  107. static void
  108. store_buffer(p, nb, atom)
  109. char    *p;
  110. int    nb;
  111. Atom    atom;
  112. {
  113.     XChangeProperty(dpy, root, atom, XA_STRING,
  114.         8, PropModeReplace, (uchar *) p, nb);
  115. }
  116.  
  117. /*
  118.  * Add an atom to the program's atom cache.
  119.  */
  120. static Atom
  121. get_atom(n, ifexists)
  122. int    n, ifexists;
  123. {
  124.     char    tmp[32];
  125.  
  126.     if (n >= natoms) {
  127.         atom = (Atom *) XtRealloc((char *) atom, (n+1) * sizeof(Atom));
  128.         while (natoms < n+1)
  129.             atom[natoms++] = 0;
  130.     }
  131.     if (!atom[n]) {
  132.         _sprintf(tmp, "CUT_BUFFER%d", n);
  133.         atom[n] = XInternAtom(dpy, tmp, (Bool) ifexists);
  134.     }
  135.     return atom[n];
  136. }
  137.  
  138. /*
  139.  * Draw a string in the window with top-left corner justification.
  140.  */
  141. static void
  142. place_text(cb, str, len, y)
  143. CbWidget    cb;
  144. char        *str;
  145. int        len, y;
  146. {
  147.     int    cols;
  148.     GC    gc;
  149.  
  150.     gc = (cb->hilite)? cb->gc_inv: cb->gc;
  151.     if (y <= (int) cb->core.height) {
  152.         cols = ((int) cb->core.width + cb->font_height - 1)
  153.                         / cb->font_width;
  154.         len = min(len, cols);
  155.         if (len > 0) {
  156.             y += cb->font->ascent;
  157.             XDrawImageString(dpy, cb->core.window, gc,
  158.                         0, y, str, len);
  159.         }
  160.     }
  161. }
  162.  
  163. /*
  164.  * ============================================================================
  165.  * The following collection of functions and data structures define
  166.  * the cb widget.  Each cb widget displays a single cut buffer value
  167.  * in a window, and provides cut and paste access to that buffer.
  168.  */
  169.  
  170. static void
  171. cb_initialize(req, wdg, args, nargs)                /*ARGSUSED*/
  172. Widget        req, wdg;
  173. ArgList        args;
  174. Cardinal    *nargs;
  175. {
  176.     CbWidget    cb    = (CbWidget) wdg;
  177.  
  178.     cb->font_width = cb->font->max_bounds.width;
  179.     cb->font_height = cb->font->ascent + cb->font->descent;
  180.     cb->gc = 0;
  181.     cb->gc_inv = 0;
  182.     cb->hilite = 0;
  183.  
  184.     XChangeProperty(dpy, root, cb->atom, XA_STRING,
  185.         8, PropModeAppend, (uchar *) "", 0);
  186. }
  187.  
  188. static void
  189. cb_realize(wdg, mask, attrs)
  190. Widget            wdg;
  191. XtValueMask        *mask;
  192. XSetWindowAttributes    *attrs;
  193. {
  194.     CbWidget    cb    = (CbWidget) wdg;
  195.     XtGCMask    v_mask    = 0L;
  196.     XGCValues    values;
  197.  
  198.     XtCreateWindow(wdg, InputOutput, CopyFromParent, *mask, attrs);
  199.  
  200.     values.font = cb->font->fid;
  201.     values.foreground = cb->fgnd;
  202.         values.background = cb->core.background_pixel;
  203.     v_mask = GCFont | GCForeground | GCBackground;
  204.     cb->gc = XtGetGC(wdg, v_mask, &values);
  205.  
  206.         values.foreground = cb->core.background_pixel;
  207.     values.background = cb->fgnd;
  208.     cb->gc_inv = XtGetGC(wdg, v_mask, &values);
  209. }
  210.  
  211. /*
  212.  * Redraw the contents of one of the subwindows.
  213.  * The function assumes the cut buffer contains text data, and parses
  214.  * it accordingly.  The data is split into lines at each '\n' character.
  215.  * Lines which extend beyond the subwindow's borders are clipped; no
  216.  * wrap-around processing is done.
  217.  * Keep it simple.
  218.  */
  219. static void
  220. cb_redisplay(wdg, event, region)                /*ARGSUSED*/
  221. Widget        wdg;
  222. XEvent        *event;
  223. Region        region;
  224. {
  225.     CbWidget    cb    = (CbWidget) wdg;
  226.     char         *p, *pp, *base;
  227.     int        y, nbytes;
  228.  
  229.     y = 0;
  230.     p = pp = base = fetch_buffer(cb->atom, &nbytes);
  231.     while (pp < base + nbytes) {
  232.         if (*pp == '\n') {
  233.             place_text(cb, p, pp - p, y);
  234.             p = pp + 1;
  235.             y += cb->font_height;
  236.         }
  237.         pp++;
  238.     }
  239.     place_text(cb, p, pp - p, y);
  240.     XFree(base);
  241. }
  242.  
  243. static void
  244. cb_destroy(wdg)
  245. Widget    wdg;
  246. {
  247.     CbWidget    cb    = (CbWidget) wdg;
  248.  
  249.     XtReleaseGC(wdg, cb->gc);
  250.     XtReleaseGC(wdg, cb->gc_inv);
  251. }
  252.  
  253. /*
  254.  * Make this widget the owner of the PRIMARY selection.
  255.  * The window contents are then redrawn with highlighting.
  256.  * It seems that if a XSetSelectionOwner is performed on a client's
  257.  * window, no SelectionClear event is generated if another window
  258.  * within the same client is already the selection owner.
  259.  * To force generation of the SelectionClear event, this function
  260.  * sets the selection ownership to None before taking it itself.
  261.  */
  262. static void
  263. cb_cut(wdg, event, parms, nparms)                /*ARGSUSED*/
  264. Widget        wdg;
  265. XEvent        *event;
  266. String        *parms;
  267. Cardinal    *nparms;
  268. {
  269.     CbWidget    cb    = (CbWidget) wdg;
  270.     Window        win    = cb->core.window;
  271.  
  272.     if (cb->hilite == 1)
  273.         return;
  274.  
  275.     XSetSelectionOwner(dpy, XA_PRIMARY, None, event->xbutton.time);
  276.     XSetSelectionOwner(dpy, XA_PRIMARY, win, event->xbutton.time);
  277.  
  278.     if (XGetSelectionOwner(dpy, XA_PRIMARY) == win) {
  279.         cb->hilite = 1;
  280.         XClearArea(dpy, win, 0,0,0,0, False);
  281.         cb_redisplay(wdg, (XEvent *)0, (Region)0);
  282. #ifdef XVIEW
  283.         XSetSelectionOwner(dpy, caret, win, event->xbutton.time);
  284.         XSetSelectionOwner(dpy, clipboard, win, event->xbutton.time);
  285. #endif
  286.     }
  287. }
  288.  
  289. static void
  290. cb_paste(wdg, event, parms, nparms)                /*ARGSUSED*/
  291. Widget        wdg;
  292. XEvent        *event;
  293. String        *parms;
  294. Cardinal    *nparms;
  295. {
  296.     CbWidget    cb    = (CbWidget) wdg;
  297.     Window        w;
  298.     char        *ptr;
  299.     int        n;
  300.  
  301.     w = XGetSelectionOwner(dpy, XA_PRIMARY);
  302.     if (w == None) {
  303.         ptr = fetch_buffer(atom[0], &n);    /* copy from cb0 */
  304.         store_buffer(ptr, n, cb->atom);
  305.         XFree(ptr);
  306.     }
  307.     else if (w != cb->core.window)
  308.         XConvertSelection(dpy, XA_PRIMARY, XA_STRING,
  309.                 cb->atom, root, event->xbutton.time);
  310. }
  311.  
  312. static void
  313. cb_clear(wdg, event, parms, nparms)                /*ARGSUSED*/
  314. Widget        wdg;
  315. XEvent        *event;
  316. String        *parms;
  317. Cardinal    *nparms;
  318. {
  319.     CbWidget    cb    = (CbWidget) wdg;
  320.  
  321.     store_buffer("", 0, cb->atom);
  322.     if (cb->hilite) {
  323.         cb->hilite = 0;
  324.         XSetSelectionOwner(dpy, XA_PRIMARY, None, event->xbutton.time);
  325.     }
  326. }
  327.  
  328. static void
  329. cb_rotate(wdg, event, parms, nparms)                /*ARGSUSED*/
  330. Widget        wdg;
  331. XEvent        *event;
  332. String        *parms;
  333. Cardinal    *nparms;
  334. {
  335.     int    n = 0;
  336.  
  337.     if (*nparms > 0)
  338.         n = atoi(parms[0]);
  339.     if (n != 0)
  340.         XRotateWindowProperties(dpy, root, atom, natoms, n);
  341. }
  342.  
  343. static void
  344. cb_quit(wdg, event, parms, nparms)                /*ARGSUSED*/
  345. Widget        wdg;
  346. XEvent        *event;
  347. String        *parms;
  348. Cardinal    *nparms;
  349. {
  350.     exit(0);
  351. }
  352.  
  353. /*
  354.  * Clear and redraw the widget's window.
  355.  */
  356. static void
  357. cb_refresh(wdg, event, parms, nparms)                /*ARGSUSED*/
  358. Widget        wdg;
  359. XEvent        *event;
  360. String        *parms;
  361. Cardinal    *nparms;
  362. {
  363.     XClearArea(dpy, wdg->core.window, 0,0,0,0, False);
  364.     cb_redisplay(wdg, (XEvent *)0, (Region)0);
  365. }
  366.  
  367. /*
  368.  * Someone or something wants a copy of the current PRIMARY selection.
  369.  * Such a request is only satisfied if the target type is STRING.
  370.  * (No conversion facilities are provided by this program).
  371.  * The selection request is met by copying the current contents
  372.  * of the cut buffer to the target window+atom.
  373.  */
  374. static void
  375. cb_selreq(wdg, event, parms, nparms)                /*ARGSUSED*/
  376. Widget        wdg;
  377. XEvent        *event;
  378. String        *parms;
  379. Cardinal    *nparms;
  380. {
  381.     int            nbytes;
  382.     char            *ptr;
  383.     XSelectionEvent        notify;
  384.     XSelectionRequestEvent    *rq;
  385.     CbWidget        cb    = (CbWidget) wdg;
  386. #ifdef XVIEW
  387.     unsigned long        data;
  388. #endif
  389.  
  390.     rq = (XSelectionRequestEvent *) event;
  391.  
  392.     notify.type = SelectionNotify;
  393.     notify.display = rq->display;
  394.     notify.requestor = rq->requestor;
  395.     notify.selection = rq->selection;
  396.     notify.target = rq->target;
  397.     notify.property = None;
  398.     notify.time = rq->time;
  399.  
  400. #ifdef XVIEW
  401.         if (rq->selection == XA_PRIMARY) {
  402.         if (rq->target == yield) {
  403.             /* tell 'em we'll give it up */
  404.             data = 1;
  405.             XChangeProperty(dpy, rq->requestor, rq->property,
  406.                     rq->target, 32, PropModeReplace,
  407.                     (uchar *) &data, 1);
  408.             notify.property = rq->property;
  409.         }
  410.         else {
  411. #endif        
  412.  
  413.     if (cb->hilite && rq->target == XA_STRING) {
  414.         ptr = fetch_buffer(cb->atom, &nbytes);
  415.         XChangeProperty(dpy, rq->requestor, rq->property, XA_STRING,
  416.             8, PropModeReplace, (uchar *) ptr, nbytes);
  417.         notify.property = rq->property;
  418.         XFree(ptr);
  419.     }
  420.  
  421. #ifdef XVIEW
  422.         }
  423.     }
  424.     else if (rq->selection == caret) {
  425.         if (rq->target == yield) {
  426.             /*
  427.              * Give up the caret (which meant that we
  428.              * own the clipboard)
  429.              */
  430.             XSetSelectionOwner(dpy, caret, None, 
  431.                     event->xselectionrequest.time);
  432.             data = 1;
  433.             XChangeProperty(dpy, rq->requestor, rq->property,
  434.                     rq->target, 32, PropModeReplace,
  435.                     (uchar *) &data, 1);
  436.             notify.property = rq->property;
  437.         }
  438.     }
  439.     else if (rq->selection == clipboard && cb->hilite) {
  440.         ptr = fetch_buffer(cb->atom, &nbytes);
  441.         if (rq->target == lengthc || rq->target == length) {
  442.             /*
  443.              * Send the length of the selection.
  444.              */
  445.             data = nbytes;
  446.             XChangeProperty(dpy, rq->requestor, rq->property,
  447.                     rq->target, 32, PropModeReplace,
  448.                     (uchar *) &data, 1);
  449.             notify.property = rq->property;
  450.         }
  451.         else if (rq->target == XA_STRING || 
  452.             rq->target == text || rq->target == ctext) {
  453.             /*
  454.              * Send the selection itself.
  455.              * All of our selections will be XA_STRING,
  456.              * but if they ask for COMPOUND_TEXT, it's ok
  457.              * to say that that's what we've got...
  458.              */
  459.             XChangeProperty(dpy, rq->requestor, rq->property, 
  460.                 (rq->target == ctext? ctext: XA_STRING), 8,
  461.                 PropModeReplace, ptr, nbytes);
  462.             notify.property = rq->property;
  463.         }
  464.         XFree(ptr);
  465.     }
  466. #endif
  467.  
  468.     XSendEvent(dpy, rq->requestor, False, 0, (XEvent *) ¬ify);
  469. }
  470.  
  471. /*
  472.  * Boo hiss, someone has taken the PRIMARY selection ownership
  473.  * away from this widget.  The current window contents must
  474.  * be redrawn without highlighting.
  475.  */
  476. static void
  477. cb_selclear(wdg, event, parms, nparms)                /*ARGSUSED*/
  478. Widget        wdg;
  479. XEvent        *event;
  480. String        *parms;
  481. Cardinal    *nparms;
  482. {
  483.     CbWidget    cb    = (CbWidget) wdg;
  484. #ifdef XVIEW
  485.     Window        w;
  486.     Time        t;
  487. #endif
  488.  
  489.     cb->hilite = 0;
  490.     XClearArea(dpy, cb->core.window, 0,0,0,0, False);
  491.     cb_redisplay(wdg, (XEvent *)0, (Region)0);
  492. #ifdef XVIEW
  493.     /*
  494.      * Since we don't have ownership of PRIMARY anymore,
  495.      * we'd better get rid of CLIPBOARD and _SUN_SELN_CARET,
  496.      * if they're still ours.
  497.      */
  498.     t = event->xselectionclear.time;
  499.     w = XGetSelectionOwner(dpy, caret);
  500.     if (w == cb->core.window)
  501.         XSetSelectionOwner(dpy, caret, None, t);
  502.     w = XGetSelectionOwner(dpy, clipboard);
  503.     if (w == cb->core.window)
  504.         XSetSelectionOwner(dpy, clipboard, None, t);
  505. #endif
  506. }
  507.  
  508. static XtResource    resources[] = {
  509. #define offset(field)        XtOffset(CbWidget, field)
  510.     /* {name, class, type, size, offset, default_type, default_addr}, */
  511.     { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
  512.         offset(fgnd), XtRString, (XtPointer) "XtDefaultForeground"},
  513.     { XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
  514.         offset(font), XtRString, (XtPointer) "fixed" },
  515.     { XtNatom, XtCAtom, XtRAtom, sizeof(Atom),    /* internal use */
  516.         offset(atom), XtRImmediate, (XtPointer)0 },
  517. #undef offset
  518. };
  519.  
  520. static XtActionsRec    actions[] = {
  521.     { "cut",        cb_cut },
  522.     { "paste",        cb_paste },
  523.     { "clear",        cb_clear },
  524.     { "rotate",        cb_rotate },
  525.     { "quit",        cb_quit },
  526.     { "refresh",        cb_refresh },
  527.     { "selreq",        cb_selreq },
  528.     { "selclear",        cb_selclear },
  529. };
  530.  
  531. static char    cb_transl[] = "\
  532.     <Btn1Down>:        cut() \n\
  533.     Shift <Btn2Down>:    clear() \n\
  534.     <Btn2Down>:        paste() \n\
  535.     Shift <Btn3Down>:    rotate(-1) \n\
  536.     <Btn3Down>:        rotate(1) \n\
  537.     <Key>Left:        rotate(-1) \n\
  538.     <Key>Right:        rotate(1) \n\
  539.     <Key>Up:        rotate(-1) \n\
  540.     <Key>Down:        rotate(1) \n\
  541.     <Key>q:            quit() \n\
  542.     <SelReq>:        selreq() \n\
  543.     <SelClr>:        selclear() \n\
  544.     ";
  545.  
  546. CbClassRec    cbClassRec = {
  547.     {
  548.     (WidgetClass) &widgetClassRec,        /* superclass */
  549.     "Buffer",                /* class_name */
  550.     sizeof(CbRec),                /* widget_size */
  551.     NULL,                    /* class_initialize */
  552.     NULL,                    /* class_part_initialize */
  553.     FALSE,                    /* class_inited */
  554.     cb_initialize,                /* initialize */
  555.     NULL,                    /* initialize_hook */
  556.     cb_realize,                /* realize */
  557.     actions,                /* actions */
  558.     XtNumber(actions),            /* num_actions */
  559.     resources,                /* resources */
  560.     XtNumber(resources),            /* num_resources */
  561.     NULLQUARK,                /* xrm_class */
  562.     TRUE,                    /* compress_motion */
  563.     TRUE,                    /* compress_exposure */
  564.     TRUE,                    /* compress_enterleave */
  565.     FALSE,                    /* visible_interest */
  566.     cb_destroy,                /* destroy */
  567.     NULL,                    /* resize */
  568.     cb_redisplay,                /* expose */
  569.     NULL,                    /* set_values */
  570.     NULL,                    /* set_values_hook */
  571.     XtInheritSetValuesAlmost,        /* set_values_almost */
  572.     NULL,                    /* get_values_hook */
  573.     NULL,                    /* accept_focus */
  574.     XtVersion,                /* version */
  575.     NULL,                    /* callback_private */
  576.     cb_transl,                /* tm_table */
  577.     XtInheritQueryGeometry,            /* query_geometry */
  578.     XtInheritDisplayAccelerator,        /* display_accelerator */
  579.     NULL                    /* extension */
  580.     },
  581. };
  582.  
  583. WidgetClass    cbWidgetClass = (WidgetClass) &cbClassRec;
  584.  
  585. /*
  586.  * Here endeth the section concerned with the cb widget.
  587.  * Normal viewing shall now be resumed.
  588.  * ============================================================================
  589.  */
  590.  
  591.  
  592. static void
  593. usage()
  594. {
  595.     _fprintf(stderr,
  596.         "Usage: %s [Xt option] [-l layout] [-n count] [-p|-s list] [-r count]\n",
  597.         PGM_NAME);
  598.     exit(1);
  599. }
  600.  
  601. /*
  602.  * Gracefully exit after an XIO error.
  603.  * This avoids messy error messages sometimes seen from xterm
  604.  * or in the xdm-errors file when forcibly destroying the client program.
  605.  */
  606. static int
  607. xioerror(d)                            /*ARGSUSED*/
  608. Display    *d;
  609. {
  610.     exit(1);                        /*NOTREACHED*/
  611. }
  612.  
  613. /*
  614.  * Print the contents of a cut buffer on stdout.
  615.  */
  616. static void
  617. doprint(n, ptr, nb)
  618. int    n;
  619. char    *ptr;
  620. int    nb;
  621. {
  622.     Atom    a;
  623.  
  624.     a = get_atom(n, True);
  625.     if (a) {
  626.         ptr = fetch_buffer(a, &nb);
  627.         if (write(1, ptr, nb) != nb) {
  628.             _fprintf(stderr, "Write error\n");
  629.             exit(1);
  630.         }
  631.         XFree(ptr);
  632.     }
  633. }
  634.  
  635. /*
  636.  * Load a new value into one of the cut buffers.
  637.  */
  638. static void
  639. doset(n, ptr, nb)
  640. int    n;
  641. char    *ptr;
  642. int    nb;
  643. {
  644.     store_buffer(ptr, nb, get_atom(n, False));
  645. }
  646.  
  647. /*
  648.  * Process an ASCII list of cut buffer numbers.
  649.  * Lists must obey the form "buffno[,buffno...]"
  650.  * where buffno is a non-negative integer or a range
  651.  * of the form M-N.  A processing function is called
  652.  * for each buffer number in the list.  Duplicates and
  653.  * list ordering is significant.
  654.  */
  655. static void
  656. dolist(list, fn, data, nbytes)
  657. char    *list;
  658. void    (*fn)();
  659. char    *data;
  660. int    nbytes;
  661. {
  662.     int    m, n, x;
  663.  
  664.     while (*list) {
  665.         if (!isdigit(*list))
  666.             usage();
  667.         for (m = 0; isdigit(*list); list++)
  668.             m = m * 10 + *list - '0';
  669.  
  670.         (*fn)(m, data, nbytes);
  671.  
  672.         if (*list == '-') {
  673.             list++;
  674.             if (!isdigit(*list))
  675.                 usage();
  676.             for (n = 0; isdigit(*list); list++)
  677.                 n = n * 10 + *list - '0';
  678.  
  679.             x = (m > n)? -1: 1;
  680.             while (m != n) {
  681.                 m += x;
  682.                 (*fn)(m, data, nbytes);
  683.             }
  684.         }
  685.  
  686.         if (*list == ',')
  687.             list++;
  688.         else if (*list)
  689.             usage();
  690.     }
  691. }
  692.  
  693. /*
  694.  * Perform a task mode command, i.e.
  695.  * do something to the cut buffers immediately,
  696.  * without the need to create any X windows first.
  697.  */
  698. static void
  699. dotask(cmd, arg)
  700. int    cmd;
  701. char    *arg;
  702. {
  703.     char    *ptr;
  704.     int    i, n, nb;
  705.  
  706.     ptr = (char *)0;
  707.     n = nb = 0;
  708.  
  709.     switch (cmd) {
  710.     case 'p':            /* print one or more buffers */
  711.         dolist(arg, doprint, (char *)0, 0);
  712.         break;
  713.     case 'r':            /* rotate the buffer contents */
  714.         n = atoi(arg);
  715.         if (n == 0)
  716.             break;
  717.         for (i = nbuffs - 1; i >= 0; i--)
  718.             (void) get_atom(i, False);
  719.         for (i = nbuffs - 1; i >= 0; i--)
  720.             XChangeProperty(dpy, root, atom[i], XA_STRING,
  721.                 8, PropModeAppend, (uchar *) "", 0);
  722.  
  723.         XRotateWindowProperties(dpy, root, atom, nbuffs, n);
  724.         break;
  725.     case 's':            /* store data in one or more buffers */
  726.         do {
  727.             ptr = XtRealloc(ptr, nb + BUFINC);
  728.             i = BUFINC;
  729.             do {
  730.                 n = read(0, ptr + nb, i);
  731.                 nb += n;
  732.                 i -= n;
  733.             } while (n > 0 && i > 0);
  734.         } while (n > 0);
  735.         if (n == -1) {
  736.             _fprintf(stderr, "Read error\n");
  737.             exit(1);
  738.         }
  739.         dolist(arg, doset, ptr, nb);
  740.         XtFree(ptr);
  741.         break;
  742.     }
  743. }
  744.  
  745. typedef struct {
  746.     int    nbuffs;
  747.     char    *layout;
  748. } ares_t, *ares_ptr;
  749.  
  750. static ares_t    ares;
  751.  
  752. static XtResource    res[] = {
  753. #define offset(field)        XtOffset(ares_ptr, field)
  754.     { XtNbufferCount, XtCBufferCount, XtRInt, sizeof(int), 
  755.         offset(nbuffs), XtRImmediate, (XtPointer) 8 },
  756.     { XtNlayout, XtCLayout, XtRString, sizeof(char *),
  757.         offset(layout), XtRImmediate, "horiz" },
  758. #undef offset
  759. };
  760.  
  761. static char        *def[] = {        /* default resource values */
  762.     ".bufferCount:        8",
  763.     ".layout:        horizontal",
  764.     "*font:            fixed",
  765.     "*Buffer.width:        60",
  766.     "*Buffer.height:    60",
  767.     0,
  768. };
  769.  
  770. static XrmOptionDescRec opt[] = {
  771.     { "-n",    ".bufferCount",    XrmoptionSepArg, (caddr_t) 8 },
  772.     { "-l",    ".layout",    XrmoptionSepArg, (caddr_t) "horiz" },
  773. };
  774.  
  775. /*
  776.  * Parse the command line options, and
  777.  * perform all the windows initializations.
  778.  */
  779. static void 
  780. init()
  781. {
  782.     int    i, n;
  783.     char    **p;
  784.     char    *attach = 0;
  785.     char    name[16];
  786.     Arg    args[2];
  787.  
  788.     /*
  789.      * Set up the atoms that we already know about.
  790.      */
  791.     natoms = 8;
  792.     atom = (Atom *) XtMalloc(natoms * sizeof(Atom));
  793.     atom[0] = XA_CUT_BUFFER0;
  794.     atom[1] = XA_CUT_BUFFER1;
  795.     atom[2] = XA_CUT_BUFFER2;
  796.     atom[3] = XA_CUT_BUFFER3;
  797.     atom[4] = XA_CUT_BUFFER4;
  798.     atom[5] = XA_CUT_BUFFER5;
  799.     atom[6] = XA_CUT_BUFFER6;
  800.     atom[7] = XA_CUT_BUFFER7;
  801.  
  802.     /*
  803.      * Initialize the toolkit, parse the command line,
  804.      * initialize the resources database, and find out
  805.      * how many buffers to deal with.
  806.      */
  807.     top = XtAppInitialize(&app, PGM_CLASS, opt, 2, &argc, argv, def, 0, 0);
  808.     dpy = XtDisplay(top);
  809.     root = RootWindow(dpy, DefaultScreen(dpy));
  810.  
  811.     XtGetApplicationResources(top, &ares, res, XtNumber(res), 0, 0);
  812.     nbuffs = max(ares.nbuffs, 1);
  813.  
  814.     /*
  815.      * If the command line contains one of the task mode
  816.      * switches (print, set, rotate), it is processed here.
  817.      * If there are multiple task mode args, only the first
  818.      * one is processed.
  819.      */
  820.     for (p = argv + 1; p < argv + argc; p++) {
  821.         if (eq(*p, "-p") || eq(*p, "-r") || eq(*p, "-s")) {
  822.             if (p == argv + argc - 1)
  823.                 usage();
  824.  
  825.             dotask(p[0][1], p[1]);
  826.             XCloseDisplay(dpy);
  827.             exit(0);
  828.         }
  829.     }
  830.  
  831.     /*
  832.      * If no task mode request has been made of us, this code
  833.      * proceeds with the rest of the windows initialization.
  834.      * The container widget is created, the sub-widgets are
  835.      * created and then everything is realized.
  836.      */
  837.     if (argc > 1)
  838.         usage();
  839.     if (ares.layout[0] == 'h')
  840.         attach = XtNfromHoriz;
  841.     if (ares.layout[0] == 'v')
  842.         attach = XtNfromVert;
  843.  
  844.     wdg = (Widget *) XtMalloc(nbuffs * sizeof(Widget));
  845.  
  846.     box = XtCreateWidget("container", formWidgetClass, top, 0, 0);
  847.     XtManageChild(box);
  848.  
  849.     for (i = 0; i < nbuffs; i++) {
  850.         XtSetArg(args[0], XtNatom, get_atom(i, False));
  851.         n = 1;
  852.         if (attach && i > 0) {
  853.             XtSetArg(args[1], attach, wdg[i-1]);
  854.             n = 2;
  855.         }
  856.  
  857.         _sprintf(name, "buffer%d", i);
  858.         wdg[i] = XtCreateWidget(name, cbWidgetClass, box, args, n);
  859.         XtManageChild(wdg[i]);
  860.     }
  861.  
  862.     XSelectInput(dpy, root, PropertyChangeMask);
  863.     XSetIOErrorHandler(xioerror);
  864.  
  865.     XtRealizeWidget(top);
  866.  
  867. #ifdef XVIEW
  868.     clipboard = XInternAtom(dpy, "CLIPBOARD", False);
  869.     caret = XInternAtom(dpy, "_SUN_SELN_CARET", False);
  870.     yield = XInternAtom(dpy, "_SUN_SELN_YIELD", False);
  871.     length = XInternAtom(dpy, "LENGTH", False);
  872.     lengthc = XInternAtom(dpy, "LENGTH_CHARS", False);
  873.     text = XInternAtom(dpy, "TEXT", False);
  874.     ctext = XInternAtom(dpy, "COMPOUND_TEXT", False);
  875. #endif
  876. }
  877.  
  878. /*
  879.  * Process incoming events.
  880.  * The program needs to know when a cut buffer value changes.
  881.  * We achieve this by eavesdropping on PropertyNotify events arising
  882.  * on the root window.  If such an event is delivered to us, the
  883.  * function immediately clears the window displaying that property
  884.  * and causes an Expose event to be generated for the window.
  885.  * This is horrible nasty stuff.
  886.  */
  887. static void
  888. xevents()
  889. {
  890.     XEvent    event;
  891.     int    i;
  892.  
  893.     for (;;) {
  894.         XtAppNextEvent(app, &event);
  895.         if (event.type != PropertyNotify) {
  896.             (void) XtDispatchEvent(&event);
  897.             continue;
  898.         }
  899.  
  900.         for (i = 0; i < natoms; i++)
  901.             if (event.xproperty.atom == atom[i])
  902.                 XClearArea(dpy, XtWindow(wdg[i]), 0,0,0,0,True);
  903.     }
  904. }
  905.  
  906. main(_argc, _argv)
  907. int    _argc;
  908. char    **_argv;
  909. {
  910.     argc = _argc;
  911.     argv = _argv;
  912.  
  913.     init();
  914.     xevents();
  915.     return 0;
  916. }
  917.